
#include "PersistentQ.h"

void DEBUGPrint();
PersistentQ::PersistentQ():m_pcFileName(NULL),m_hMapFile(INVALID_HANDLE_VALUE),m_pBuf(NULL),m_pBufAllocation(NULL),
    m_hFile(INVALID_HANDLE_VALUE), m_pnCount(NULL),
    m_pnHeadIndex(NULL),m_pnTailIndex(NULL),m_eMode(eUnknown),m_nElemCount(0)
{
}
PersistentQ::~PersistentQ()
{
    close();
}
void PersistentQ::close()
{
#if defined   __linux__
    if (NULL != m_pBuf)
    {
        if (munmap(m_pBuf, nMaxMappedFileSize) == -1) 
        {
            DEV;
        }
    }
    if (INVALID_HANDLE_VALUE !=m_hFile)
    {
        ::close(m_hMapFile); // close the file mapping object
    }
    if((NULL != m_pcFileName) && (0 == size ()))
    {
        int iRet = unlink (m_pcFileName);
        if (iRet== 0)
        {
            cout<<"All data processed.Removed map file." <<endl;
        }
    }

#elif defined _WIN32 || defined _WIN64
    if (NULL != m_pBuf)
    {
        UnmapViewOfFile(m_pBuf);
    }
    if (INVALID_HANDLE_VALUE != m_hMapFile)
    {
        CloseHandle(m_hMapFile); // close the file mapping object
    }
    if (INVALID_HANDLE_VALUE !=m_hFile)
    {
        CloseHandle(m_hFile); // close the file mapping object
    }
    if((NULL != m_pcFileName) && (0 == size ()))
    {
        int iRet = DeleteFile((LPCWSTR)m_pcFileName);
        if (iRet > 0)
        {
            cout<<"All data processed.Removed map file." <<endl;
        }
    }
#endif
}
HANDLE PersistentQ::OpenOrCreateFile(const char* pcFileName )
{

    HANDLE hFile = INVALID_HANDLE_VALUE;
#if defined   __linux__
#elif defined _WIN32 || defined _WIN64

    hFile = CreateFile((LPCWSTR)pcFileName, //lpFileName
        GENERIC_READ | GENERIC_WRITE,//dwDesiredAccess
        FILE_SHARE_WRITE | FILE_SHARE_READ,////dwShareMode
        NULL,//lpSecurityAttributes
        OPEN_EXISTING,//dwCreationDisposition
        FILE_ATTRIBUTE_NORMAL,//dwFlagsAndAttributes
        NULL //hTemplateFile
        );
    if (INVALID_HANDLE_VALUE != hFile)
    {
        cout<<"Opening existing mapped file."<<endl;
        return hFile;
    }
    else if (INVALID_HANDLE_VALUE == hFile)
    {
        cout<<"Mapped file not present." <<endl;
        hFile = CreateFile((LPCWSTR)pcFileName,
            GENERIC_READ | GENERIC_WRITE,//dwDesiredAccess
            FILE_SHARE_WRITE | FILE_SHARE_READ,////dwShareMode
            NULL,
            CREATE_NEW,
            FILE_ATTRIBUTE_NORMAL,
            NULL);

        if (INVALID_HANDLE_VALUE != hFile)
        {
            cout<<"Mapped file created." <<endl;
        }
        return hFile;
    }
#endif
    return hFile;
}
bool PersistentQ::OpenMappedFileReader(const char*pcFileName)

{
    bool bStat = false;
    do
    {
#if defined   __linux__
        int nFD = open(pcFileName, //path
            O_RDWR,//flags
            S_IRUSR| S_IWUSR ); //mode
        if (-1  == nFD)
        {
            break;
        }
        if (lseek(nFD,//file descriptor
            nMaxMappedFileSize - 1,//offset
            SEEK_SET) // whence);
            == -1)
        {
            break;
        }
        m_hFile = nFD;
#elif defined _WIN32 || defined _WIN64

        HANDLE hFile = CreateFile((LPCWSTR)pcFileName,
            GENERIC_READ | GENERIC_WRITE,
            FILE_SHARE_WRITE | FILE_SHARE_READ,
            NULL,
            OPEN_EXISTING,
            FILE_ATTRIBUTE_NORMAL,
            NULL);
        if (INVALID_HANDLE_VALUE != hFile)
        {
            cout<<"Opening existing mapped file."<<endl;
        }
        else
        {
            DEV;
            break;
        }
        m_hFile = hFile;

        m_hMapFile = CreateFileMapping( m_hFile,
            NULL, // default security
            PAGE_READWRITE, // read/write permission
            0, // size of mapping object, high
            nMaxMappedFileSize, // size of mapping object, low
            NULL); // name of mapping object

        if (m_hMapFile == NULL)
        {
            DEV;
            break;
        }
#endif
        m_pcFileName = (char*)pcFileName;
        cout<< "File mapping object successfully openred for reading."<<endl;
        bStat = true;
    } while(0);
    if (false == bStat)
    {
        DEBUGPrint();
    }
    return bStat;
}
bool PersistentQ::OpenMappedFileWriter(const char*pcFileName)
{
    bool bStat = false;
    do
    {
#if defined   __linux__
        int nFD = open(pcFileName, //path
            O_RDWR| O_CREAT,//flags
            S_IRUSR| S_IWUSR ); //mode
        if (-1  == nFD)
        {
            break;
        }
        if (lseek(nFD,//file descriptor
            nMaxMappedFileSize - 1,//offset
            SEEK_SET) // whence);
            == -1)
        {
            break;
        }
        m_hFile = nFD;
        write (nFD, "", 1);
#elif defined _WIN32 || defined _WIN64
        HANDLE hFile = OpenOrCreateFile(pcFileName);
        if (INVALID_HANDLE_VALUE == hFile )
        {
            break;
        }
        m_hFile = hFile;
        m_hMapFile = CreateFileMapping( m_hFile,
            NULL, // default security
            PAGE_READWRITE, // read/write permission
            0, // size of mapping object, high
            nMaxMappedFileSize, // size of mapping object, low
            NULL); // name of mapping object

        if (m_hMapFile == NULL)
        {
            break;
        }
#endif
        m_pcFileName = (char*)pcFileName;
        cout<< "File mapping object successfully created for writing."<<endl;
        bStat = true;
    }
    while(0);
    if (false == bStat )
    {
        DEBUGPrint();
    }
    return bStat;
}
bool PersistentQ::init(const char * pcFileName, QMode eMode)
{
    bool bStat = false;
    do
    {
        if ( eWriter == eMode )
        {
            bStat = OpenMappedFileWriter(pcFileName);
        }
        else if ( eReader  == eMode )
        {
            bStat = OpenMappedFileReader(pcFileName);
        }
        else
        {
            break;
        }
        if (false == bStat)
        {
            break;
        }

#if defined   __linux__

        m_pBuf = mmap(NULL, nMaxMappedFileSize, (PROT_READ| PROT_WRITE),  MAP_SHARED, m_hFile, 0); 
        //void *addr, size_t length, int prot, int flags,int fd, off_t offset
        if (MAP_FAILED == m_pBuf) 
        {
            break;           
        }

#elif defined _WIN32 || defined _WIN64

        m_pBuf = (void*) MapViewOfFile(m_hMapFile, // handle to map object
            FILE_MAP_ALL_ACCESS, // read/write permission
            0,
            0,
            nBuffSize);

        if (m_pBuf == NULL)
        {
            break;
        }
#endif
        //count
        m_pnCount = ((int*) m_pBuf) + 1;
        //head
        m_pnHeadIndex = ((int*) m_pBuf) + 2;

        //tail
        m_pnTailIndex = ((int*) m_pBuf) + 3;

        m_nElemCount = *m_pnCount;
        //fist chunck is reserved
        m_pBufAllocation = (char*)((char*)m_pBuf + 1* (sizeof(Data)));
        m_eMode = eMode;
        cout <<"Current elements " <<*m_pnCount << " current head index  " <<  *m_pnHeadIndex << " current tail index  " << *m_pnTailIndex <<endl;
        bStat = true;
    } while(0);
    return bStat;
}

void* PersistentQ:: GetBlock(size_t nReQSize )
{
    void* vRet = NULL;
    if ((NULL== m_pnHeadIndex) || (NULL == m_pnHeadIndex) || (NULL == m_pnTailIndex))
    {
        return vRet;
    }
    vRet = (void*) ((char*)m_pBufAllocation + (*m_pnTailIndex) * nReQSize);
    return vRet;
}

void PersistentQ::ReleaseBlock(void* pvData)
{
    if ( NULL != pvData)
    {
        memset(pvData,0,sizeof(Data));
    }
}

bool PersistentQ::PushBack(Data& dt)
{
    bool bStatus = false;
    do
    {
        //check q full or not
        m_nElemCount = *m_pnCount;
        if ( nMaxChunckCount == m_nElemCount)
        {
            cout << "Q is full.Entry count  " <<m_nElemCount <<endl;
            break;
        }
        void* pdt  = NULL;
        pdt = GetBlock(sizeof(Data));
        if (NULL == pdt)
        {
            break;
        }
        memcpy(pdt, &dt, sizeof(Data));
        *m_pnTailIndex = ((*m_pnTailIndex) + 1);
        if ( (*m_pnTailIndex)< nMaxChunckCount)
        {
            //ok
        }
        else
        {
            //rewind
            *m_pnTailIndex  = 0;
        }
        m_nElemCount += 1;
        *m_pnCount = m_nElemCount;
        cout <<" Pushed. Current elements " <<*m_pnCount << " current head index  " <<  *m_pnHeadIndex << " current tail index  " << *m_pnTailIndex <<endl;
        bStatus = true;
    } while(0);
    return bStatus;
}

bool PersistentQ::GetFront(Data& dt)
{
    bool bStatus = false;
    do
    {
        // check if q empty
        m_nElemCount =  *m_pnCount;
        if (0 == m_nElemCount)
        {
            cout << "Q is empty " <<endl;
            break;
        }
        Data* pdt = NULL;
        pdt = (Data*) ( (char*)m_pBufAllocation + (*m_pnHeadIndex) * sizeof(Data));
        memcpy(&dt,pdt,sizeof(Data));

        ReleaseBlock(pdt);

        m_nElemCount -= 1;
        *m_pnCount = m_nElemCount;
        *m_pnHeadIndex = (*m_pnHeadIndex) + 1;
        if( (*m_pnHeadIndex) <nMaxChunckCount)
        {
            //ok
        }
        else
        {
            //rewind
            *m_pnHeadIndex = 0;
        }
        cout <<" Popped. Current elements " <<*m_pnCount << " current head index  " <<  *m_pnHeadIndex << " current tail index  " << *m_pnTailIndex <<endl;
        bStatus = true;
    } while(0);

    return bStatus;
}

int PersistentQ::size()
{
    return m_nElemCount;
}
